﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using System.IO;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Input;

namespace TheFugitive
{
    class Level
    {
        private Tile[,] tiles;
        private Texture2D[] layers;
        private SoundEffect exitReachedSound;
        public List<Texture2D> life = new List<Texture2D>();

        public bool isType3;

        private float damagedTime;
        private float damagedTime2;
        
        // 배경화면 스크롤을 위한 X 좌표
        private float scrollX1 = 0.0f;
        private float scrollX2 = 800.0f;

        private const int EntityLayer = 1;

        Car car;

        public Player Player
        {
            get { return player; }
        }
        Player player;

        private List<Enemy> enemies = new List<Enemy>();  // enemy

        public TimeSpan TimeRemaining
        {
            get { return timeRemaining; }
        }
        TimeSpan timeRemaining;

        private const int PointsPerSecond = 5;

        // Level content.        
        public ContentManager Content
        {
            get { return content; }
        }
        ContentManager content;

        public bool isAiEnemy;
        public int enemyTime;
        public int enemyType;
        public int typeCnt;
        /// <summary>
        /// Constructs a new level.
        /// </summary>
        /// <param name="serviceProvider">
        /// The service provider that will be used to construct a ContentManager.
        /// </param>
        /// <param name="fileStream">
        /// A stream containing the tile data.
        /// </param>
        public Level(IServiceProvider serviceProvider, Stream fileStream, int levelIndex)
        {
            // Create a new content manager to load content used just by this level.
            content = new ContentManager(serviceProvider, "Content");

            timeRemaining = TimeSpan.FromMinutes(2.0);

            LoadTiles(fileStream);

            // 배경 텍스처 로드, 현재 하나의 배경만 넣어서 하나만 읽도록 배열 수정
            layers = new Texture2D[2];
            for (int i = 0; i < layers.Length; ++i)
            {
                // 현재 레벨은 하나!
                int segmentIndex = levelIndex;
                layers[i] = Content.Load<Texture2D>("Backgrounds/Layer" + i + "_" + segmentIndex);
            }

            // 플레이어 목숨심볼 로드, 현재 4개
            for (int i = 0; i < 4; i++)
            {
                life.Add(Content.Load<Texture2D>("Sprites/Player/Life"));
            }

            // 사운드 로드
            exitReachedSound = Content.Load<SoundEffect>("Sounds/ExitReached");

            // 시작위치 임시로 지정해둠 (현재 항상고정)
            Vector2 start = new Vector2(200, 320);
            player = new Player(this, start);

            start = new Vector2(100, 380);
            Enemy enemy = new Enemy(this, start, 0);
            enemies.Add(enemy);
            enemyType++;

            Random r = new Random(DateTime.Now.Millisecond);
            int direction = r.Next(0, 1);   // 0 : left  1: right
            if (direction == 0)
                start = new Vector2(800, 480);
            else
                start = new Vector2(0, 480);
            car = new Car(this, start, direction);
        }

        /// <summary>
        /// Jason | 120526
        /// 레벨에서 나타날 모든 객체 그리기
        /// </summary>
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            // 하늘 배경 그리기
            spriteBatch.Draw(layers[0], new Vector2(0.0f, 0.0f), Color.White);

            // 도시 배경의 스크롤, 전체 이미지 가로 크기 1500px, 스마트폰 가로 크기 800px
            spriteBatch.Draw(layers[1], new Vector2(scrollX1--, 0.0f), Color.White);
            if (scrollX1 < -700)
            {
                spriteBatch.Draw(layers[1], new Vector2(scrollX2--, 0.0f), Color.White);
                if (scrollX2 < 0)
                {
                    scrollX1 = 0.0f;
                    scrollX2 = 800.0f;
                }
            }

            // 플레이어 목숨심볼 그리기
            for (int i = 0; i < life.Count; ++i)
                spriteBatch.Draw(life[i], new Vector2(20.0f + i * 30, 20.0f), Color.White);

            foreach (Obstacle obstacle in obstacles)
                obstacle.Draw(gameTime, spriteBatch);

            Player.Draw(gameTime, spriteBatch);

            // 적 캐릭터 그리기
            foreach (Enemy enemy in enemies)
                enemy.Draw(gameTime, spriteBatch);

            // 타일 그리기
            DrawTiles(spriteBatch);

            //DrawLine(spriteBatch);

            // 차 그리기
            car.Draw(gameTime, spriteBatch);
        }

        public void DrawLine(SpriteBatch spriteBatch, SpriteFont hudFont)
        {
            // For each tile position
            for (int y = 0; y < Height; ++y)
            {
                for (int x = 0; x < Width; ++x)
                {
                    Texture2D texture = tiles[x, y].Texture;
                    if (texture != null)
                    {
                        Vector2 position = new Vector2(x, y) * Tile.Size;
                        spriteBatch.DrawString(hudFont, "o", position, Color.Red);
                    }
                }
            }
        }

        /// <summary>
        /// Jaosn | 120526
        /// 타일 그리기
        /// </summary>
        private void DrawTiles(SpriteBatch spriteBatch)
        {
            // For each tile position
            for (int y = 0; y < Height; ++y)
            {
                for (int x = 0; x < Width; ++x)
                {
                    // 선택된 지점에 텍스처가 없다면
                    Texture2D texture = tiles[x, y].Texture;
                    if (texture != null)
                    {
                        // 화면에 그리기
                        Vector2 position = new Vector2(x, y) * Tile.Size;
                        spriteBatch.Draw(texture, position, Color.White);
                    }
                }
            }
        }

        /// <summary>
        /// 레벨 텍스트 읽어 LoadTile 함수로 보내기
        /// </summary>
        /// <param name="fileStream"></param>
        private void LoadTiles(Stream fileStream)
        {
            // 레벨을 로드하고 레벨 텍스트 내 모든 줄의 길이가 동일한지 확인
            int width;
            // 레벨 텍스트에서 한줄의 길이
            List<string> lines = new List<string>();
            using (StreamReader reader = new StreamReader(fileStream))
            {
                string line = reader.ReadLine();
                width = line.Length;
                while (line != null)
                {
                    lines.Add(line);
                    if (line.Length != width)
                        throw new Exception(String.Format("The length of line {0} is different from all preceeding lines.", lines.Count));
                    line = reader.ReadLine();
                }
            }

            // 레벨 텍스트 라인의 수가 높이가 됨
            // 라인의 길이와 수로 그리드를 만듬
            tiles = new Tile[width, lines.Count];

            // 각 타일 위치에 맞춰진 레벨 텍스트 상의 문자를 LoadTile로 넘김
            for (int y = 0; y < Height; ++y)
            {
                for (int x = 0; x < Width; ++x)
                {
                    char tileType = lines[y][x];
                    tiles[x, y] = LoadTile(tileType, x, y);
                }
            }
        }

        /// <summary>
        /// Jason | 120526
        /// 레벨 텍스트의 문자에 따라 타일을 로드하고 속성을 결정
        /// </summary>
        /// <param name="tileType">
        /// 로드된 레벨 텍스트의 문자
        /// </param>
        /// <param name="x">
        /// 타일의 x 위치
        /// </param>
        /// <param name="y">
        /// 타일의 y 위치
        /// </param>
        /// <returns>타일 로드</returns>
        private Tile LoadTile(char tileType, int x, int y)
        {
            switch (tileType)
            {
                // 빈 공간
                case '.':
                    return new Tile(null, TileCollision.Passable);

                // 막힌 블록
                case '#':
                    return LoadTile("BlockA", 1, TileCollision.Impassable);
                
                // 장애물
                case '^':
                    return LoadObstacleTile(x, y);

                // 알 수 없는 문자일 때
                default:
                    throw new NotSupportedException(String.Format("지원하지 않는 유형의 문자 '{0}' at position {1}, {2}.", tileType, x, y));
            }
        }

        /// <summary>
        /// 파라미터 정보로 실제 타일 텍스처를 불러옴
        /// </summary>
        /// <param name="name">
        /// 타일 종류
        /// </param>
        /// <param name="collision">
        /// 타일 번호
        /// </param>
        /// <returns>The new tile.</returns>
        private Tile LoadTile(string name, int index, TileCollision collision)
        {
            string blockName = name + index;
            return new Tile(Content.Load<Texture2D>("Tiles/" + blockName), collision);
        }


        #region Bounds and collision

        /// <summary>
        /// Gets the collision mode of the tile at a particular location.
        /// This method handles tiles outside of the levels boundries by making it
        /// impossible to escape past the left or right edges, but allowing things
        /// to jump beyond the top of the level and fall off the bottom.
        /// </summary>
        public TileCollision GetCollision(int x, int y)
        {
            // Prevent escaping past the level ends.
            if (x < 0 || x >= Width)
                return TileCollision.Impassable;
            // Allow jumping past the level top and falling through the bottom.
            if (y < 0 || y >= Height)
                return TileCollision.Passable;

            return tiles[x, y].Collision;
        }

        /// <summary>
        /// Gets the bounding rectangle of a tile in world space.
        /// </summary>        
        public Rectangle GetBounds(int x, int y)
        {
            return new Rectangle(x * Tile.Width, y * Tile.Height, Tile.Width, Tile.Height);
        }

        /// <summary>
        /// Width of level measured in tiles.
        /// </summary>
        public int Width
        {
            get { return tiles.GetLength(0); }
        }

        /// <summary>
        /// Height of the level measured in tiles.
        /// </summary>
        public int Height
        {
            get { return tiles.GetLength(1); }
        }

        #endregion


        /// <summary>
        /// object update
        /// </summary>
        /// <param name="keyboardState">
        /// 눌린키
        /// </param>
        /// <param name="accelState"></param>
        /// <param name="orientation"></param>
        public void Update(GameTime gameTime,KeyboardState keyboardState, AccelerometerState accelState, DisplayOrientation orientation)
        {
            //enemyTime++;
            //if (enemyTime % 330 == 0)
            //{
            //    if (enemyType == 3)
            //    {
            //        Vector2 start = (Vector2)player.positionList[0];
            //        Enemy enemy = new Enemy(this, start, true);
            //        enemies.Add(enemy);
            //        isAiEnemy = true;
            //    }
            //    else
            //    {
            //        Random r = new Random(DateTime.Now.Millisecond);
            //        int x = r.Next(0, 800);
            //        int y = r.Next(80, 480);
            //        Vector2 start = new Vector2(x, y);
            //        Enemy enemy = new Enemy(this, start, enemyType);
            //        enemies.Add(enemy);
            //    }

            //    enemyType++;
            //}
            if (enemies[0].life == 0)
            {
                enemies.RemoveAt(0);
            }


            if (enemies.Count == 0)
            {
                if (enemyType == 3)
                {
                    isType3 = true;
                }
                if (enemyType == 4)
                {
                    Vector2 start = new Vector2(800, 480);
                    Enemy enemy = new Enemy(this, start,  true);
                    enemies.Add(enemy);
                }
                else
                {
                    Vector2 start = new Vector2(800, 480);
                    Enemy enemy = new Enemy(this, start, enemyType);
                    enemies.Add(enemy);
                }

                enemyType++;

            }

            if (isType3)
            {
                typeCnt++;
            }

            if (typeCnt == 330)
            {
                Vector2 start = new Vector2(800, 480);
                Enemy enemy = new Enemy(this, start, true);
                enemies.Add(enemy);
            }
 
            // 여기에 현재 게임진행상황에 따라 if else로 구분
            if ((life.Count == 0) || TimeRemaining == TimeSpan.Zero)
            {
                // Still want to perform physics on the player.
                Player.ApplyPhysics(gameTime);
            }
            else
            {
                timeRemaining -= gameTime.ElapsedGameTime;
                Player.Update(gameTime, keyboardState, accelState, orientation);

                Random r = new Random(DateTime.Now.Millisecond);
                int random = r.Next(0, 100);

                //if (player.movementList.Count == 20 && !isAiEnemy)
                //{
                //    Vector2 start = (Vector2)player.positionList[0];
                //    Enemy enemy = new Enemy(this, start, true);
                //    enemies.Add(enemy);
                //    isAiEnemy = true;
                //}
                
                car.Update(gameTime);
                // 장애물에 관련된 업데이트

                UpdateObstacles(gameTime);
                UpdateEnemies(gameTime);

                if (player.positionList.Count == 20)
                {
                    player.movementList.RemoveAt(0);
                    player.positionList.RemoveAt(0);
                }
                
            }
            // Clamp the time remaining at zero.
            if (timeRemaining < TimeSpan.Zero)
                timeRemaining = TimeSpan.Zero;
        }

        /// <summary>
        /// Animates each enemy and allow them to kill the player.
        /// </summary>
        private void UpdateEnemies(GameTime gameTime)
        {
            // insert to enemy 생성 알고리즘


            foreach (Enemy enemy in enemies)
            {
                enemy.Update(gameTime, player);

                // Touching an enemy instantly kills the player
                if (enemy.BoundingRectangle.Intersects(Player.BoundingRectangle))
                {
                    //OnPlayerKilled(enemy);
                    damagedTime += (float)gameTime.ElapsedGameTime.TotalSeconds;

                    KeyboardState keyboardState = Keyboard.GetState();
                    if (keyboardState.IsKeyDown(Keys.Space))
                        break;

                    if (life.Count == 0)
                        break;

                    float initValue = 0.03333333f;
                    // 0.33초 간격으로 체크하여 플레이어 목숨을 하나 제거
                    if (damagedTime < initValue + 0.02f)
                    {
                        life.RemoveAt(life.Count - 1);
                    }
                    else if (damagedTime > initValue * 10 && damagedTime < initValue * 10 + initValue)
                    {
                        life.RemoveAt(life.Count - 1);
                    }
                    else if (damagedTime > initValue * 20 && damagedTime < initValue * 20 + initValue)
                    {
                        life.RemoveAt(life.Count - 1);
                    }
                    else if (damagedTime > initValue * 30 && damagedTime < initValue * 30 + initValue)
                    {
                        life.RemoveAt(life.Count - 1);
                    }

                    player.IsDamaging = true;
                    player.playerDamaged(gameTime);
                }
                else
                {
                    player.IsDamaging = false;
                }
            }
        }

        // ------------------- 장애물 추가 ----------------

        private List<Obstacle> obstacles = new List<Obstacle>();

        /// <summary>
        /// Jason | 120609
        /// 장애물 타일 로드
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private Tile LoadObstacleTile(int x, int y)
        {
            Point position = GetBounds(x, y).Center;
            obstacles.Add(new Obstacle(this, new Vector2(position.X, position.Y)));

            return new Tile(null, TileCollision.Passable);
        }

        /// <summary>
        /// Jason | 120609
        /// 충돌체크를 위한 장애물의 가상영역
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public Rectangle ObstacleBounds(int x, int y)
        {
            return new Rectangle(x * Tile.Width, y * Tile.Height, Tile.Width, Tile.Height);
        }

        /// <summary>
        /// Jason | 120609
        /// 장애물 충돌에 관련된 업데이트
        /// </summary>
        /// <param name="gameTime"></param>
        private void UpdateObstacles(GameTime gameTime)
        {
            for (int i = 0; i < obstacles.Count; ++i)
            {
                Obstacle obstacle = obstacles[i];

                obstacle.Update(gameTime);
                Vector2 depth = RectangleExtensions.GetIntersectionDepth(obstacle.BoundingRectangle, player.BoundingRectangle);
                Vector2 depth2 = RectangleExtensions.GetIntersectionDepth(obstacle.BoundingRectangle, enemies[0].BoundingRectangle);
                if (depth != Vector2.Zero )
                {
                    damagedTime += (float)gameTime.ElapsedGameTime.TotalSeconds;

                    KeyboardState keyboardState = Keyboard.GetState();
                    if (keyboardState.IsKeyDown(Keys.Space))
                        break;

                    if (life.Count == 0)
                        break;

                    float initValue = 0.03333333f;
                    // 0.33초 간격으로 체크하여 플레이어 목숨을 하나 제거
                    if (damagedTime < initValue + 0.02f)
                    {
                        life.RemoveAt(life.Count - 1);
                    }
                    else if (damagedTime > initValue * 10 && damagedTime < initValue * 10 + initValue)
                    {
                        life.RemoveAt(life.Count - 1);
                    }
                    else if (damagedTime > initValue * 20 && damagedTime < initValue * 20 + initValue)
                    {
                        life.RemoveAt(life.Count - 1);
                    }
                    else if (damagedTime > initValue * 30 && damagedTime < initValue * 30 + initValue)
                    {
                        life.RemoveAt(life.Count - 1);
                    }

                    player.IsDamaging = true;
                    player.playerDamaged(gameTime);
                }
                else if (depth == Vector2.Zero)
                {
                    player.IsDamaging = false;
                }

                
                if (depth2 != Vector2.Zero)
                {

                    damagedTime2 += (float)gameTime.ElapsedGameTime.TotalSeconds;

                    KeyboardState keyboardState = Keyboard.GetState();
                    if (keyboardState.IsKeyDown(Keys.Space))
                        break;

                    if (enemies[0].life == 0)
                        break;

                    float initValue = 0.05555555f;
                    // 0.33초 간격으로 체크하여 플레이어 목숨을 하나 제거
                    if (damagedTime2 < initValue + 0.02f)
                    {
                        enemies[0].life--;
                    }
                    else if (damagedTime2 > initValue * 10 && damagedTime2 < initValue * 10 + initValue)
                    {
                        enemies[0].life--;
                    }
                    else if (damagedTime2 > initValue * 20 && damagedTime2 < initValue * 20 + initValue)
                    {
                        enemies[0].life--;
                    }
                    else if (damagedTime2 > initValue * 30 && damagedTime2 < initValue * 30 + initValue)
                    {
                        enemies[0].life--;
                    }
                    enemies[0].isDamaging = true;
                    enemies[0].agnetDamaged(gameTime);
                    
                    
                }
                
            }
        }
    }
}